package com.naico.web.action;

import com.naico.core.pojo.*;
import com.naico.core.service.ActualPaymentService;
import com.naico.core.service.CalculatorService;
import com.naico.core.service.ChartGeneratorService;
import com.naico.core.service.RollDownPaymentService;
import com.naico.core.service.comparator.AscendingMonthsToPayOffComparator;
import com.naico.core.service.comparator.DebtOrderComparator;
import com.naico.util.Utils;
import com.naico.web.form.DebtInfoForm;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.DispatchAction;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.data.category.CategoryDataset;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * User: Dawood Ajaz
 * Date: Jan 9, 2009
 * Time: 7:14:19 PM
 */
public class CalculatorAction extends DispatchAction {


    public ActionForward setupPaymentPlan(ActionMapping mapping,
                                          ActionForm form,
                                          HttpServletRequest request,
                                          HttpServletResponse response) throws Exception {

        return mapping.findForward("success-setup");
    }

    public ActionForward generatePaymentPlan(ActionMapping mapping,
                                             ActionForm form,
                                             HttpServletRequest request,
                                             HttpServletResponse response) throws Exception {
        DebtInfoForm debtInfoForm = (DebtInfoForm) form;

        DebtInformationRequest debtInfoRequest = createDebtInformationRequest(debtInfoForm);
        DebtInformationResult results = new DebtInformationResult();
        //TODO: check if any months to payoff is zero, or empty, we will have fill it with actual payment plan term.

        //TODO: Actual payment to be calculated from Actual Payment plan.
        CalculatorService actualPaymentService = new ActualPaymentService();
        ActualPaymentPlan actualPaymentPlan = (ActualPaymentPlan) actualPaymentService.generate(debtInfoRequest);
        // add actual payment into results
        results.add(actualPaymentPlan);

        // fill month to pay off from the actual schedule items size.
        // set months to payoff for rolldown payment schedule,
        // it is calculating through actual payment plan
        fillMonthsToPayOffIntoForm(debtInfoForm, actualPaymentPlan);

        // fill months to pay off into debt information request.
        fillMonthsToPayOffInDebtInfoRequest(debtInfoForm, debtInfoRequest);

        CalculatorService service = new RollDownPaymentService();
        // generate payment service
        RollDownPaymentPlan rollDownPaymentPlan = (RollDownPaymentPlan) service.generate(debtInfoRequest);
        // add roll down payment into results
        results.add(rollDownPaymentPlan);

        //request.setAttribute("results", results);
        request.getSession().setAttribute("results", results);

        return mapping.findForward("success-plan");
    }

    /**
     * Clears session and reset input vars
     *
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public ActionForward clearSession(ActionMapping mapping,
                                      ActionForm form,
                                      HttpServletRequest request,
                                      HttpServletResponse response) throws Exception {

        request.getSession().invalidate();
        return mapping.findForward("success-setup");
    }

    private static DebtInformationRequest createDebtInformationRequest(DebtInfoForm debtInfoForm) {
        // Fill the debt information.
        List<DebtInformation> debtInfoList = fillDebtInformation(debtInfoForm);

        // create new debt information request and fill with debt information list
        DebtInformationRequest debtInfoRequest = new DebtInformationRequest();
        debtInfoRequest.setDebtInformationList(debtInfoList);
        debtInfoRequest.setExtraPayment(debtInfoForm.getExtraPayment());

        // Fixed Debt order, will always based on the months to pay off.
        DebtOrderComparator debtOrder = new AscendingMonthsToPayOffComparator();
        debtInfoRequest.setDebtOrder(debtOrder);

        return debtInfoRequest;
    }

    private static List<DebtInformation> fillDebtInformation(DebtInfoForm debtInfoForm) {
        List<DebtInformation> debtInfoList = new ArrayList<DebtInformation>();
        String[] creditors = debtInfoForm.getCreditor();

        // fill inputs into debtInformation object
        for (int i = 0; i < creditors.length; i++) {
            // TODO: check array size and array index out of bound exception
            String creditor = debtInfoForm.getCreditor()[i];
            Double balance = debtInfoForm.getBalance()[i];
            Double minimumPayment = debtInfoForm.getMinimumPayment()[i];
            //TODO: we are currently using actual payment as minimum payment.
            Double actualPayment = debtInfoForm.getMinimumPayment()[i];
            Double rate = debtInfoForm.getRate()[i];
            Integer monthsToPayOff = debtInfoForm.getMonthsToPayOff()[i];

            // calculate 4 percent of balance as minimum payment
            if (minimumPayment == 0.0) {
                minimumPayment = (balance * 4 / 100);
            }

            // checking  null or empty for string values
            // checking null or zero for double values
            if (!Utils.isNullOrEmpty(creditor) &&
                    !Utils.isNullOrZero(balance) &&
                    !Utils.isNullOrZero(minimumPayment) &&
                    !Utils.isNullOrZero(rate)) {

                //TODO: start date as new date, needs to are we taking input from user or it will be new date
                DebtInformation debtInfo = new DebtInformation(new Date(), creditor, balance,
                        rate, minimumPayment, actualPayment, monthsToPayOff);
                debtInfoList.add(debtInfo);
            }
        }
        return debtInfoList;
    }

    private static void fillMonthsToPayOffIntoForm(DebtInfoForm debtInfoForm, ActualPaymentPlan actualPaymentPlan) {

        List<PaymentSchedule> paymentSchedules = actualPaymentPlan.getPaymentSchedules();
        // get existing months to pay off from deb info form.
        Integer[] monthsToPayOff = debtInfoForm.getMonthsToPayOff();
        int[] filledInputs = getFilledInputPos(debtInfoForm);
        // set months to payoff for rolldown payment schedule, it is calculating through actual payment plan
        for (int i = 0; i < paymentSchedules.size(); i++) {
            PaymentSchedule schedule = paymentSchedules.get(i);
            int pos = filledInputs[i];
            monthsToPayOff[pos] = schedule.getPaymentScheduleItems().size();
        }
        debtInfoForm.setMonthsToPayOff(monthsToPayOff);
    }

    /**
     * Gets which inputs in array are filled
     * tries to get this value by guessing if user has entered any creditor name, if yes which index
     *
     * @param debtInfoForm
     * @return returns int[] containing filled indexes
     */
    private static int[] getFilledInputPos(DebtInfoForm debtInfoForm) {
        String[] filledCreditors = debtInfoForm.getCreditor();
        int[] retInts = new int[filledCreditors.length];
        for (int i = 0, j = 0; i < filledCreditors.length; i++) {
            if (filledCreditors[i] != null && filledCreditors[i].length() > 0) {
                retInts[j++] = i;
            }
        }
        return retInts;
    }

    private static void fillMonthsToPayOffInDebtInfoRequest(DebtInfoForm debtInfoForm, DebtInformationRequest request) {
        // get existing months to pay off from deb info form.
        Integer[] monthsToPayOff = debtInfoForm.getMonthsToPayOff();
        List<DebtInformation> debtInfoList = request.getDebtInformationList();

        // set months to payoff for rolldown payment schedule, it is calculating through actual payment plan
        for (int i = 0; i < debtInfoList.size(); i++) {
            DebtInformation debInfo = debtInfoList.get(i);
            // check if month of pay is zero, then set from acutal payment plan
            debInfo.setMonthsToPayOff(monthsToPayOff[i]);
        }
        debtInfoForm.setMonthsToPayOff(monthsToPayOff);
    }

    public ActionForward createChart(ActionMapping mapping,
                                     ActionForm form,
                                     HttpServletRequest request,
                                     HttpServletResponse response) throws Exception {

        OutputStream out = null;
        try {

            DebtInformationResult results = (DebtInformationResult) request.getSession().getAttribute("results");

            List<PaymentPlan> paymentPlans = results.getPaymentPlans();

            // set the content type so the browser can see this as a picture
            response.setContentType("image/png");

            // generate chart service
            ChartGeneratorService chartService = new ChartGeneratorService();
            CategoryDataset dataSet = chartService.createDataset(paymentPlans);
            JFreeChart chart = chartService.createChart(dataSet);
            // getting output stream from response
            out = response.getOutputStream();
            // writing the chart to outputstream in png format
            ChartUtilities.writeChartAsPNG(out, chart, 600, 300);
        }
        catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            if (out != null) {
                out.close();
            }
        }
        return mapping.findForward("success-chart");
    }

}